From 912e2bc761d7d57bd33d68cb5050da43dea59636 Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Fri, 24 Oct 2025 11:42:16 +0900
Subject: (김준회) ecc/mdg 테스트 페이지 수정
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/[lng]/admin/ecc/page.tsx | 474 +++++++++++++++++++++++++++++++++----------
 1 file changed, 365 insertions(+), 109 deletions(-)

(limited to 'app/[lng]/admin/ecc/page.tsx')

diff --git a/app/[lng]/admin/ecc/page.tsx b/app/[lng]/admin/ecc/page.tsx
index 257ecfea..cd09e213 100644
--- a/app/[lng]/admin/ecc/page.tsx
+++ b/app/[lng]/admin/ecc/page.tsx
@@ -10,7 +10,7 @@ import { Label } from '@/components/ui/label'
 import { Badge } from '@/components/ui/badge'
 import { Separator } from '@/components/ui/separator'
 import { Alert, AlertDescription } from '@/components/ui/alert'
-import { Loader2, Play, CheckCircle, XCircle, Send } from 'lucide-react'
+import { Loader2, Play, CheckCircle, XCircle, Send, Plus, Minus } from 'lucide-react'
 import { toast } from 'sonner'
 
 // SOAP 송신 함수들 import
@@ -125,36 +125,96 @@ export default function ECCSenderTestPage() {
 
   // PO 생성 테스트
   const [poData, setPoData] = useState({
-    // 헤더 정보
-    ANFNR: 'TEST001',
-    LIFNR: '1000000001',
-    ZPROC_IND: 'A',
-    ANGNR: 'TEST001',
-    WAERS: 'KRW',
-    ZTERM: '0001',
-    INCO1: 'FOB',
-    INCO2: 'Seoul, Korea',
-    MWSKZ: 'V0',
-    LANDS: 'KR',
-    ZRCV_DT: '20241201',
-    ZATTEN_IND: 'Y',
-    IHRAN: '20241201',
-    TEXT: 'Test PO Creation',
-    // 아이템 정보
-    ANFPS: '00001',
-    NETPR: '1000.00',
-    PEINH: '1',
-    BPRME: 'EA',
-    NETWR: '1000.00',
-    BRTWR: '1100.00',
-    LFDAT: '20241201',
-    // PR 반환 정보
-    EBELN: 'PR001',
-    EBELP: '00001',
-    MSGTY: 'S',
-    MSGTXT: 'Test message'
+    header: {
+      ANFNR: 'TEST001',
+      LIFNR: '1000000001',
+      ZPROC_IND: 'A',
+      ANGNR: 'TEST001',
+      WAERS: 'KRW',
+      ZTERM: '0001',
+      INCO1: 'FOB',
+      INCO2: 'Seoul, Korea',
+      MWSKZ: 'V0',
+      LANDS: 'KR',
+      ZRCV_DT: '20241201',
+      ZATTEN_IND: 'Y',
+      IHRAN: '20241201',
+      TEXT: 'Test PO Creation',
+      LSTEL: '',
+      VSTEL: '',
+      ZDLV_CNTLR: '',
+      ZDLV_PRICE_NOTE: '',
+      ZDLV_PRICE_T: ''
+    },
+    items: [{
+      ANFNR: 'TEST001',
+      ANFPS: '00001',
+      LIFNR: '1000000001',
+      NETPR: '1000.00',
+      PEINH: '1',
+      BPRME: 'EA',
+      NETWR: '1000.00',
+      BRTWR: '1100.00',
+      LFDAT: '20241201',
+      EBELP: '',
+      ZCON_NO_PO: ''
+    }],
+    prReturn: [{
+      ANFNR: 'TEST001',
+      ANFPS: '00001',
+      EBELN: 'PR001',
+      EBELP: '00001',
+      MSGTY: 'S',
+      MSGTXT: 'Test message'
+    }]
   })
 
+  // PO 아이템 관리 함수들
+  const addPoItem = () => {
+    const newItemIndex = poData.items.length + 1
+    setPoData(prev => ({
+      ...prev,
+      items: [...prev.items, {
+        ANFNR: prev.header.ANFNR,
+        ANFPS: String(newItemIndex).padStart(5, '0'),
+        LIFNR: prev.header.LIFNR,
+        NETPR: '0.00',
+        PEINH: '1',
+        BPRME: 'EA',
+        NETWR: '0.00',
+        BRTWR: '0.00',
+        LFDAT: prev.header.IHRAN,
+        EBELP: '',
+        ZCON_NO_PO: ''
+      }]
+    }))
+  }
+
+  const removePoItem = (index: number) => {
+    if (poData.items.length > 1) {
+      setPoData(prev => ({
+        ...prev,
+        items: prev.items.filter((_, i) => i !== index)
+      }))
+    }
+  }
+
+  const updatePoItem = (index: number, field: string, value: string) => {
+    setPoData(prev => ({
+      ...prev,
+      items: prev.items.map((item, i) =>
+        i === index ? { ...item, [field]: value } : item
+      )
+    }))
+  }
+
+  const updatePoHeader = (field: string, value: string) => {
+    setPoData(prev => ({
+      ...prev,
+      header: { ...prev.header, [field]: value }
+    }))
+  }
+
   return (
     <div className="container mx-auto py-8 space-y-6">
       <div className="flex items-center justify-between">
@@ -513,87 +573,188 @@ export default function ECCSenderTestPage() {
                 PO (Purchase Order) 생성
               </CardTitle>
               <CardDescription>
-                구매주문 생성 요청을 ECC로 전송합니다. (IF_ECC_EVCP_PO_CREATE)
+                구매주문 생성 요청을 ECC로 전송합니다. (IF_ECC_EVCP_PO_CREATE) - 하나의 PO에 여러 PR 아이템들을 포함시키는 구조
               </CardDescription>
             </CardHeader>
-            <CardContent className="space-y-4">
+            <CardContent className="space-y-6">
+              {/* PO 헤더 정보 */}
               <div className="space-y-4">
-                <h4 className="font-semibold">PO 헤더 정보</h4>
-                <div className="grid grid-cols-2 gap-4">
+                <div className="flex items-center justify-between">
+                  <h4 className="font-semibold">PO 헤더 정보</h4>
+                  <Badge variant="outline" className="text-xs">
+                    {poData.items.length}개 아이템
+                  </Badge>
+                </div>
+                <div className="grid grid-cols-3 gap-4">
                   <div className="space-y-2">
-                    <Label htmlFor="po-anfnr">입찰번호 (필수)</Label>
+                    <Label htmlFor="po-anfnr">입찰번호 (ANFNR) <span className="text-red-500">*</span></Label>
                     <Input
                       id="po-anfnr"
-                      value={poData.ANFNR}
-                      onChange={(e) => setPoData(prev => ({ ...prev, ANFNR: e.target.value }))}
-                      placeholder="입찰번호"
+                      value={poData.header.ANFNR}
+                      onChange={(e) => updatePoHeader('ANFNR', e.target.value)}
+                      placeholder="입찰번호 (ANFNR)"
                     />
                   </div>
                   <div className="space-y-2">
-                    <Label htmlFor="po-lifnr">공급업체 계정 (필수)</Label>
+                    <Label htmlFor="po-lifnr">공급업체 계정 (LIFNR) : 벤더코드 <span className="text-red-500">*</span></Label>
                     <Input
                       id="po-lifnr"
-                      value={poData.LIFNR}
-                      onChange={(e) => setPoData(prev => ({ ...prev, LIFNR: e.target.value }))}
-                      placeholder="공급업체 계정번호"
+                      value={poData.header.LIFNR}
+                      onChange={(e) => updatePoHeader('LIFNR', e.target.value)}
+                      placeholder="공급업체 계정 (LIFNR) : 벤더코드"
                     />
                   </div>
                   <div className="space-y-2">
-                    <Label htmlFor="po-zproc">처리상태 (필수)</Label>
+                    <Label htmlFor="po-zproc">처리상태 (ZPROC_IND) <span className="text-red-500">*</span></Label>
                     <Input
                       id="po-zproc"
-                      value={poData.ZPROC_IND}
-                      onChange={(e) => setPoData(prev => ({ ...prev, ZPROC_IND: e.target.value }))}
-                      placeholder="구매처리상태"
+                      value={poData.header.ZPROC_IND}
+                      onChange={(e) => updatePoHeader('ZPROC_IND', e.target.value)}
+                      placeholder="처리상태 (ZPROC_IND)"
                     />
                   </div>
                   <div className="space-y-2">
-                    <Label htmlFor="po-waers">통화 (필수)</Label>
+                    <Label htmlFor="po-angnr">협상번호 (ANGNR)</Label>
+                    <Input
+                      id="po-angnr"
+                      value={poData.header.ANGNR}
+                      onChange={(e) => updatePoHeader('ANGNR', e.target.value)}
+                      placeholder="협상번호 (ANGNR)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-waers">통화 (WAERS) <span className="text-red-500">*</span></Label>
                     <Input
                       id="po-waers"
-                      value={poData.WAERS}
-                      onChange={(e) => setPoData(prev => ({ ...prev, WAERS: e.target.value }))}
-                      placeholder="통화 코드"
+                      value={poData.header.WAERS}
+                      onChange={(e) => updatePoHeader('WAERS', e.target.value)}
+                      placeholder="통화 (WAERS)"
                     />
                   </div>
-                </div>
-
-                <h4 className="font-semibold">PO 아이템 정보</h4>
-                <div className="grid grid-cols-2 gap-4">
                   <div className="space-y-2">
-                    <Label htmlFor="po-anfps">입찰 아이템번호 (필수)</Label>
+                    <Label htmlFor="po-zterm">지급조건 (ZTERM) <span className="text-red-500">*</span></Label>
                     <Input
-                      id="po-anfps"
-                      value={poData.ANFPS}
-                      onChange={(e) => setPoData(prev => ({ ...prev, ANFPS: e.target.value }))}
-                      placeholder="입찰 아이템번호"
+                      id="po-zterm"
+                      value={poData.header.ZTERM}
+                      onChange={(e) => updatePoHeader('ZTERM', e.target.value)}
+                      placeholder="지급조건 (ZTERM)"
                     />
                   </div>
                   <div className="space-y-2">
-                    <Label htmlFor="po-netpr">순가격 (필수)</Label>
+                    <Label htmlFor="po-inco1">인코텀즈1 (INCO1) <span className="text-red-500">*</span></Label>
                     <Input
-                      id="po-netpr"
-                      value={poData.NETPR}
-                      onChange={(e) => setPoData(prev => ({ ...prev, NETPR: e.target.value }))}
-                      placeholder="순가격"
+                      id="po-inco1"
+                      value={poData.header.INCO1}
+                      onChange={(e) => updatePoHeader('INCO1', e.target.value)}
+                      placeholder="인코텀즈1 (INCO1)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-inco2">인코텀즈2 (INCO2) <span className="text-red-500">*</span></Label>
+                    <Input
+                      id="po-inco2"
+                      value={poData.header.INCO2}
+                      onChange={(e) => updatePoHeader('INCO2', e.target.value)}
+                      placeholder="인코텀즈2 (INCO2)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-mwskz">세금코드 (MWSKZ) <span className="text-red-500">*</span></Label>
+                    <Input
+                      id="po-mwskz"
+                      value={poData.header.MWSKZ}
+                      onChange={(e) => updatePoHeader('MWSKZ', e.target.value)}
+                      placeholder="세금코드 (MWSKZ)"
                     />
                   </div>
                   <div className="space-y-2">
-                    <Label htmlFor="po-bprme">주문단위 (필수)</Label>
+                    <Label htmlFor="po-lands">국가키 (LANDS) <span className="text-red-500">*</span></Label>
                     <Input
-                      id="po-bprme"
-                      value={poData.BPRME}
-                      onChange={(e) => setPoData(prev => ({ ...prev, BPRME: e.target.value }))}
-                      placeholder="주문단위 (예: EA)"
+                      id="po-lands"
+                      value={poData.header.LANDS}
+                      onChange={(e) => updatePoHeader('LANDS', e.target.value)}
+                      placeholder="국가키 (LANDS)"
                     />
                   </div>
                   <div className="space-y-2">
-                    <Label htmlFor="po-lfdat">납기일 (필수)</Label>
+                    <Label htmlFor="po-zrcv-dt">수령일 (ZRCV_DT) <span className="text-red-500">*</span></Label>
                     <Input
-                      id="po-lfdat"
-                      value={poData.LFDAT}
-                      onChange={(e) => setPoData(prev => ({ ...prev, LFDAT: e.target.value }))}
-                      placeholder="YYYYMMDD 형식"
+                      id="po-zrcv-dt"
+                      value={poData.header.ZRCV_DT}
+                      onChange={(e) => updatePoHeader('ZRCV_DT', e.target.value)}
+                      placeholder="수령일 (ZRCV_DT)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-zatten-ind">참석지시자 (ZATTEN_IND) <span className="text-red-500">*</span></Label>
+                    <Input
+                      id="po-zatten-ind"
+                      value={poData.header.ZATTEN_IND}
+                      onChange={(e) => updatePoHeader('ZATTEN_IND', e.target.value)}
+                      placeholder="참석지시자 (ZATTEN_IND)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-ihran">입찰마감일 (IHRAN) <span className="text-red-500">*</span></Label>
+                    <Input
+                      id="po-ihran"
+                      value={poData.header.IHRAN}
+                      onChange={(e) => updatePoHeader('IHRAN', e.target.value)}
+                      placeholder="입찰마감일 (IHRAN)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-text">텍스트 (TEXT)</Label>
+                    <Input
+                      id="po-text"
+                      value={poData.header.TEXT}
+                      onChange={(e) => updatePoHeader('TEXT', e.target.value)}
+                      placeholder="텍스트 (TEXT)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-lstel">배송지점 (LSTEL)</Label>
+                    <Input
+                      id="po-lstel"
+                      value={poData.header.LSTEL}
+                      onChange={(e) => updatePoHeader('LSTEL', e.target.value)}
+                      placeholder="배송지점 (LSTEL)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-vstel">출하지점 (VSTEL)</Label>
+                    <Input
+                      id="po-vstel"
+                      value={poData.header.VSTEL}
+                      onChange={(e) => updatePoHeader('VSTEL', e.target.value)}
+                      placeholder="출하지점 (VSTEL)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-zdlv-cntl">납품담당자 (ZDLV_CNTLR)</Label>
+                    <Input
+                      id="po-zdlv-cntl"
+                      value={poData.header.ZDLV_CNTLR}
+                      onChange={(e) => updatePoHeader('ZDLV_CNTLR', e.target.value)}
+                      placeholder="납품담당자 (ZDLV_CNTLR)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-zdlv-price-note">가격노트 (ZDLV_PRICE_NOTE)</Label>
+                    <Input
+                      id="po-zdlv-price-note"
+                      value={poData.header.ZDLV_PRICE_NOTE}
+                      onChange={(e) => updatePoHeader('ZDLV_PRICE_NOTE', e.target.value)}
+                      placeholder="가격노트 (ZDLV_PRICE_NOTE)"
+                    />
+                  </div>
+                  <div className="space-y-2">
+                    <Label htmlFor="po-zdlv-price-t">가격유형 (ZDLV_PRICE_T)</Label>
+                    <Input
+                      id="po-zdlv-price-t"
+                      value={poData.header.ZDLV_PRICE_T}
+                      onChange={(e) => updatePoHeader('ZDLV_PRICE_T', e.target.value)}
+                      placeholder="가격유형 (ZDLV_PRICE_T)"
                     />
                   </div>
                 </div>
@@ -601,6 +762,133 @@ export default function ECCSenderTestPage() {
 
               <Separator />
 
+              {/* PO 아이템 정보 */}
+              <div className="space-y-4">
+                <div className="flex items-center justify-between">
+                  <h4 className="font-semibold">PO 아이템 정보</h4>
+                  <Button
+                    onClick={addPoItem}
+                    size="sm"
+                    variant="outline"
+                    className="flex items-center gap-1"
+                  >
+                    <Plus className="h-4 w-4" />
+                    아이템 추가
+                  </Button>
+                </div>
+
+                {poData.items.map((item, index) => (
+                  <Card key={index} className="p-4">
+                    <div className="flex items-center justify-between mb-4">
+                      <h5 className="font-medium">아이템 #{index + 1}</h5>
+                      {poData.items.length > 1 && (
+                        <Button
+                          onClick={() => removePoItem(index)}
+                          size="sm"
+                          variant="destructive"
+                          className="flex items-center gap-1"
+                        >
+                          <Minus className="h-4 w-4" />
+                          삭제
+                        </Button>
+                      )}
+                    </div>
+                    <div className="grid grid-cols-3 gap-4">
+                      <div className="space-y-2">
+                        <Label>입찰번호 (ANFNR) <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.ANFNR}
+                          onChange={(e) => updatePoItem(index, 'ANFNR', e.target.value)}
+                          placeholder="입찰번호 (ANFNR)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>입찰 아이템번호 (ANFPS) <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.ANFPS}
+                          onChange={(e) => updatePoItem(index, 'ANFPS', e.target.value)}
+                          placeholder="입찰 아이템번호 (ANFPS)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>공급업체 계정 (LIFNR) : 벤더코드 <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.LIFNR}
+                          onChange={(e) => updatePoItem(index, 'LIFNR', e.target.value)}
+                          placeholder="공급업체 계정 (LIFNR) : 벤더코드"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>순가격 (NETPR) <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.NETPR}
+                          onChange={(e) => updatePoItem(index, 'NETPR', e.target.value)}
+                          placeholder="순가격 (NETPR)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>가격단위 (PEINH) <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.PEINH}
+                          onChange={(e) => updatePoItem(index, 'PEINH', e.target.value)}
+                          placeholder="가격단위 (PEINH)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>주문단위 (BPRME) <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.BPRME}
+                          onChange={(e) => updatePoItem(index, 'BPRME', e.target.value)}
+                          placeholder="주문단위 (BPRME)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>순금액 (NETWR) <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.NETWR}
+                          onChange={(e) => updatePoItem(index, 'NETWR', e.target.value)}
+                          placeholder="순금액 (NETWR)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>총금액 (BRTWR) <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.BRTWR}
+                          onChange={(e) => updatePoItem(index, 'BRTWR', e.target.value)}
+                          placeholder="총금액 (BRTWR)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>납기일 (LFDAT) <span className="text-red-500">*</span></Label>
+                        <Input
+                          value={item.LFDAT}
+                          onChange={(e) => updatePoItem(index, 'LFDAT', e.target.value)}
+                          placeholder="납기일 (LFDAT)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>구매오더 품번 (EBELP)</Label>
+                        <Input
+                          value={item.EBELP}
+                          onChange={(e) => updatePoItem(index, 'EBELP', e.target.value)}
+                          placeholder="구매오더 품번 (EBELP)"
+                        />
+                      </div>
+                      <div className="space-y-2">
+                        <Label>계약번호 (ZCON_NO_PO)</Label>
+                        <Input
+                          value={item.ZCON_NO_PO}
+                          onChange={(e) => updatePoItem(index, 'ZCON_NO_PO', e.target.value)}
+                          placeholder="계약번호 (ZCON_NO_PO)"
+                        />
+                      </div>
+                    </div>
+                  </Card>
+                ))}
+              </div>
+
+              <Separator />
+
               <div className="flex gap-4">
                 <Button
                   onClick={() => runTest('PO 생성 (샘플)', () => createTestPurchaseOrder())}
@@ -614,41 +902,9 @@ export default function ECCSenderTestPage() {
                 <Button
                   onClick={() => runTest('PO 생성 (사용자)', async () => {
                     const poRequest = {
-                      T_Bidding_HEADER: [{
-                        ANFNR: poData.ANFNR,
-                        LIFNR: poData.LIFNR,
-                        ZPROC_IND: poData.ZPROC_IND,
-                        ANGNR: poData.ANGNR,
-                        WAERS: poData.WAERS,
-                        ZTERM: poData.ZTERM,
-                        INCO1: poData.INCO1,
-                        INCO2: poData.INCO2,
-                        MWSKZ: poData.MWSKZ,
-                        LANDS: poData.LANDS,
-                        ZRCV_DT: poData.ZRCV_DT,
-                        ZATTEN_IND: poData.ZATTEN_IND,
-                        IHRAN: poData.IHRAN,
-                        TEXT: poData.TEXT
-                      }],
-                      T_Bidding_ITEM: [{
-                        ANFNR: poData.ANFNR,
-                        ANFPS: poData.ANFPS,
-                        LIFNR: poData.LIFNR,
-                        NETPR: poData.NETPR,
-                        PEINH: poData.PEINH,
-                        BPRME: poData.BPRME,
-                        NETWR: poData.NETWR,
-                        BRTWR: poData.BRTWR,
-                        LFDAT: poData.LFDAT
-                      }],
-                      T_PR_RETURN: [{
-                        ANFNR: poData.ANFNR,
-                        ANFPS: poData.ANFPS,
-                        EBELN: poData.EBELN,
-                        EBELP: poData.EBELP,
-                        MSGTY: poData.MSGTY,
-                        MSGTXT: poData.MSGTXT
-                      }]
+                      T_Bidding_HEADER: [poData.header],
+                      T_Bidding_ITEM: poData.items,
+                      T_PR_RETURN: poData.prReturn
                     }
                     return createPurchaseOrder(poRequest)
                   })}
@@ -656,7 +912,7 @@ export default function ECCSenderTestPage() {
                 >
                   {isLoading['PO 생성 (사용자)'] && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
                   <Send className="mr-2 h-4 w-4" />
-                  사용자 데이터로 생성
+                  사용자 데이터로 생성 ({poData.items.length}개 아이템)
                 </Button>
               </div>
 
-- 
cgit v1.2.3

